/******************************************************************/
/*                                                                */
/*  Winpooch : Windows Watchdog                                   */
/*  Copyright (C) 2004  Benoit Blanchon                           */
/*                                                                */
/*  This program is free software; you can redistribute it        */
/*  and/or modify it under the terms of the GNU General Public    */
/*  License as published by the Free Software Foundation; either  */
/*  version 2 of the License, or (at your option) any later       */
/*  version.                                                      */
/*                                                                */
/*  This program is distributed in the hope that it will be       */
/*  useful, but WITHOUT ANY WARRANTY; without even the implied    */
/*  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR       */
/*  PURPOSE.  See the GNU General Public License for more         */
/*  details.                                                      */
/*                                                                */
/*  You should have received a copy of the GNU General Public     */
/*  License along with this program; if not, write to the Free    */
/*  Software Foundation, Inc.,                                    */
/*  675 Mass Ave, Cambridge, MA 02139, USA.                       */
/*                                                                */
/******************************************************************/

// module's interface
#include "MainWindow.h"

// standard headers
#include <commctrl.h>
#include <tchar.h>
#include <stdio.h>

// project's headers
#include "AboutDlg.h"
#include "Alert.h"
#include "Config.h"
#include "ConfigDlg.h"
#include "HistoryWnd.h"
#include "Language.h"
#include "Log.h"
#include "Resources.h"
#include "Version.h"

// timer identifiers
#define ID_ICON			0
#define ID_ANIM_TIMER		1
#define ID_BALLOON_TIMER	2

// timer intervals
#define ANIM_LENGTH		2
#define ANIM_INTERVAL		2000
#define BALLOON_TIMEOUT		10000

// position of the mouth of poochy
#define X_MOUTH			60
#define Y_MOUTH			106

// color key used as transparent color
#define TRANSPARENT_COLOR	RGB(252,191,211)

// maximum length of a message
#define MAX_MESSAGE_LEN 256

// maximum size of the balloon
#define MAX_ALERT_LEN	2048

// user defined message
#define WM_SHELLNOTIFY  WM_USER+1

// menu item identifiers
#define IDM_SHUTDOWN	1
#define IDM_DISABLE	2
#define IDM_ENABLE	3
#define IDM_VIEWLOG	4
#define IDM_ABOUT	5
#define IDM_WEBSITE	6
#define IDM_ALERTS	7
#define IDM_REPORTBUG	8
#define IDM_CONFIG	9

typedef enum {
  STATE_NORMAL,		//< Normal state
  STATE_ALERT,		//< Alert window visible
  STATE_DISABLED,	//< Alert disabled
  STATE_NOCLICKED	//< The user didn't clicked after an alert
} WINPOOCH_STATE ;



LRESULT CALLBACK _MainWnd_WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) ;


BOOL MainWnd_RegisterClass (HINSTANCE hInstance)
{
  WNDCLASS wc = {
    .lpfnWndProc   = _MainWnd_WindowProc,
    .hInstance     = hInstance,
    .lpszClassName = WC_MAINWINDOW
  } ;
     
  return RegisterClass (&wc) ;
}

HWND MainWnd_CreateWindow (HINSTANCE hInstance)
{  
  return CreateWindowEx (WS_EX_NOACTIVATE|WS_EX_LAYERED|WS_EX_TRANSPARENT|WS_EX_TOPMOST, 
			 WC_MAINWINDOW, TEXT(APPLICATION_NAME), WS_POPUP,
			 0, 0, 1, 1, NULL, NULL, hInstance, NULL) ;
}


LRESULT CALLBACK _MainWnd_WindowProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  POINT		pt ;
  int		i ;
  PAINTSTRUCT	ps ;
  HDC		hdc ;
  RECT		rect ;
  TCHAR		szNewMessage[MAX_MESSAGE_LEN] ;
  ALERT		*pAlert ;

  static NOTIFYICONDATA nid ;
  static HMENU		hPopupMenu ;
  static HICON		aAnimIcons[ANIM_LENGTH] ;
  static HICON		hAlertIcon, hDisabledIcon ;
  static int		iAnimPos ;
  static HINSTANCE	hInstance ;
  static TCHAR		szBalloon[MAX_ALERT_LEN] ;
  static WINPOOCH_STATE state ;
  static HBITMAP	hbmpPooch ;
  static HDC		hdcImage ;
  static BITMAP		bmpInfo ;
  static HWND		hwndBalloon ;
  static TOOLINFO	tiBalloon ;
  static UINT		uTaskbarCreatedMsg ;

  switch( message )
    {

    case WM_CREATE:

      // save instance handle
      hInstance = ((CREATESTRUCT*)lParam)->hInstance ;
      
      // initial state : enabled
      state = STATE_NORMAL ;

      // build language dependant menu
      SendMessage (hwnd, WM_LANGUAGECHANGED, 0, 0) ;
       
      // load icons
      for( i=0 ; i<ANIM_LENGTH ; i++ )
	aAnimIcons[i] = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_TRAY0+i)) ;
      hAlertIcon = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ALERT)) ;
      hDisabledIcon = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_DISABLED)) ;

      // add to tray
      nid.cbSize = sizeof(nid) ;
      nid.hWnd   = hwnd ;
      nid.uID    = ID_ICON ;
      nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP ;
      nid.uCallbackMessage = WM_SHELLNOTIFY ;
      nid.hIcon  = aAnimIcons[0] ;
      lstrcpy (nid.szTip, TEXT(APPLICATION_NAME)) ;
      Shell_NotifyIcon (NIM_ADD, &nid) ;

      // create icon animation timer
      SetTimer (hwnd, ID_ANIM_TIMER, ANIM_INTERVAL, NULL) ;

      // load 
      hbmpPooch = LoadImage (hInstance, MAKEINTRESOURCE(IDB_POOCH), 
			     IMAGE_BITMAP, 0, 0, 
			     LR_CREATEDIBSECTION) ;
      hdc = GetDC (hwnd) ;
      hdcImage = CreateCompatibleDC (hdc) ;
      SelectObject (hdcImage, hbmpPooch) ;  
      ReleaseDC (hwnd, hdc) ;
      
      // get image size
      GetObject (hbmpPooch, sizeof(BITMAP), &bmpInfo) ;
      SystemParametersInfo (SPI_GETWORKAREA, 0, &rect, 0) ;  

      // set window pos
      MoveWindow (hwnd, 
		  rect.right - bmpInfo.bmWidth   - 10,
		  rect.bottom - bmpInfo.bmHeight - 10,
		  bmpInfo.bmWidth, bmpInfo.bmHeight, FALSE) ;

      // set transparancy
      SetLayeredWindowAttributes (hwnd, TRANSPARENT_COLOR, 
				  255, LWA_COLORKEY) ;   

      // create balloon box
      hwndBalloon = CreateWindow(TOOLTIPS_CLASS, NULL,
				 TTS_NOPREFIX|TTS_BALLOON,
				 0, 0, 500, 500, NULL, NULL,
				 hInstance, NULL);        
      
      // configure tool tip
      tiBalloon.cbSize = sizeof(TOOLINFO) ;
      tiBalloon.uFlags = TTF_TRACK ;
      tiBalloon.hwnd = hwnd ;
      tiBalloon.lpszText = LPSTR_TEXTCALLBACK ;	
      SendMessage(hwndBalloon, TTM_ADDTOOL, 0, (LPARAM) &tiBalloon);

      // register "TaskbarCreated" message
      uTaskbarCreatedMsg = RegisterWindowMessage (TEXT("TaskbarCreated")) ;
      
      return 0 ; // case WM_CREATE:

    case WM_ENDSESSION:
      
      Log_Print (TEXT("System shutdown")) ;
      Config_Save () ;

    case WM_DESTROY:

      KillTimer (hwnd, ID_ANIM_TIMER) ;
      for( i=0 ; i<ANIM_LENGTH ; i++ )
	CloseHandle (aAnimIcons[i]) ;
      CloseHandle (hAlertIcon) ;
      Shell_NotifyIcon (NIM_DELETE, &nid) ;
      DestroyMenu (hPopupMenu) ;
      DeleteDC (hdcImage) ;     
      DeleteObject (hbmpPooch) ;			    
      PostQuitMessage (0) ;

      return 0 ; // case WM_DESTROY:


    case WM_LANGUAGECHANGED:

      // destroy menu is needed
      if( hPopupMenu )
	DestroyMenu (hPopupMenu) ;
      
      // create menu
      hPopupMenu = CreatePopupMenu () ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_ABOUT, STR(_ABOUT)) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_WEBSITE, STR(_WEB_SITE)) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_REPORTBUG, STR(_REPORT_A_BUG)) ;
      AppendMenu (hPopupMenu, MF_SEPARATOR, 0, NULL) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_DISABLE, STR(_DISABLE)) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_VIEWLOG, STR(_VIEW_LOG)) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_ALERTS, STR(_LAST_ALERTS)) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_CONFIG, STR(_CONFIGURATION)) ;
      AppendMenu (hPopupMenu, MF_SEPARATOR, 0, NULL) ;
      AppendMenu (hPopupMenu, MF_STRING, IDM_SHUTDOWN, STR(_SHUTDOWN)) ;

      HistoryWnd_SendMessage (message, wParam, lParam) ;
      
      return 0 ; // case WM_LANGUAGECHANGED:

    case WM_COMMAND:

      if( lParam==0 )  // menu command
	{
	  switch( LOWORD(wParam) )
	    {
	    case IDM_ABOUT:
	      AboutDlg_CreateDialog (hInstance, hwnd) ;
	      break ;

	    case IDM_ENABLE:
	      state = STATE_NORMAL ;
	      nid.uFlags = NIF_ICON ;
	      nid.hIcon  = aAnimIcons[0] ;
	      Shell_NotifyIcon (NIM_MODIFY, &nid) ;
	      ModifyMenu (hPopupMenu, IDM_ENABLE, MF_BYCOMMAND,
			  IDM_DISABLE, STR(_DISABLE)) ;
	      break ;
	      
	    case IDM_DISABLE:
	      state = STATE_DISABLED ;
	      nid.uFlags = NIF_ICON ;
	      nid.hIcon  = hDisabledIcon ;
	      Shell_NotifyIcon (NIM_MODIFY, &nid) ;	
	      ModifyMenu (hPopupMenu, IDM_DISABLE, MF_BYCOMMAND,
			  IDM_ENABLE, STR(_ENABLE)) ;	  
	      break ;

	    case IDM_VIEWLOG:
	      ShellExecute (hwnd, NULL, TEXT("notepad.exe"), Log_GetPath(), NULL, SW_SHOW) ;
	      break ;

	    case IDM_WEBSITE:
	      ShellExecute (hwnd, NULL, TEXT(WEB_SITE_ADDRESS), NULL, NULL, SW_SHOW) ;
	      break ;

	    case IDM_REPORTBUG:
	      ShellExecute (hwnd, NULL, TEXT(BUG_REPORT_PAGE), NULL, NULL, SW_SHOW) ;
	      break ;
	      
	    case IDM_ALERTS:
	      HistoryWnd_CreateWindow (hInstance) ;
	      break ;

	    case IDM_CONFIG:
	      ConfigDlg_CreateDialog (hInstance, hwnd) ;
	      break ;

	    case IDM_SHUTDOWN:
	      DestroyWindow (hwnd) ;
	      break ;
	    }
	  return 0 ;
	} // menu command

      break ; // case WM_COMMAND:

    case WM_SHELLNOTIFY:

      if( wParam==ID_ICON )
	{
	  switch( lParam ) 
	    {
	    case WM_RBUTTONDOWN:
	    case WM_LBUTTONDOWN:
	      if( state==STATE_ALERT || state==STATE_NOCLICKED )
		{
		  state = STATE_NORMAL ;
		  SendMessage (hwndBalloon, TTM_TRACKACTIVATE, FALSE, (LPARAM)&tiBalloon) ;
		  KillTimer (hwnd, ID_BALLOON_TIMER) ;
		  ShowWindow (hwnd, SW_HIDE) ;
		  SendMessage (hwnd, WM_COMMAND, IDM_ALERTS, 0) ;
		}
	      else
		{		  
		  GetCursorPos (&pt) ;
		  SetForegroundWindow (hwnd) ;
		  TrackPopupMenu (hPopupMenu, TPM_RIGHTALIGN, pt.x, pt.y, 0, hwnd, NULL) ;
		  PostMessage (hwnd, WM_NULL, 0, 0) ;
		}
	      break ;

	    case WM_LBUTTONDBLCLK:
	      SendMessage (hwnd, WM_COMMAND, IDM_ALERTS, 0) ;
	      break;   
	    }
	} 

      return 0 ; // case WM_SHELLNOTIFY:

      
    case WM_PAINT:

      hdc = BeginPaint (hwnd, &ps) ;

      FillRect (hdc, &rect, (HBRUSH) (COLOR_3DFACE+1));

      BitBlt (hdc, 
	      0, 0,		// x, y
	      bmpInfo.bmWidth, bmpInfo.bmHeight,// w, h dest
	      hdcImage, 0, 0, SRCCOPY) ;	       

      EndPaint (hwnd, &ps) ;

      //InvalidateRect (hwndBalloon, NULL, FALSE) ;
      
      return 0 ; // case WM_PAINT:


    case WM_TIMER:

      switch( wParam )
	{
	case ID_ANIM_TIMER:
	  if( state==STATE_NORMAL )
	    {
	      iAnimPos++ ;
	      iAnimPos %= ANIM_LENGTH ;
	      nid.uFlags = NIF_ICON ;
	      nid.hIcon  = aAnimIcons[iAnimPos] ;
	      Shell_NotifyIcon (NIM_MODIFY, &nid) ;
	    }
	  break ;

	case ID_BALLOON_TIMER:
	  if( state==STATE_ALERT )
	    {
	      state = STATE_NOCLICKED ;
	      SendMessage (hwndBalloon, TTM_TRACKACTIVATE, FALSE, (LPARAM)&tiBalloon) ;
	      KillTimer (hwnd, ID_BALLOON_TIMER) ;
	      ShowWindow (hwnd, SW_HIDE) ;
	    }
	  break ;	  
	}

      return 0 ; // case WM_TIMER:

    case WM_ALERT:

      HistoryWnd_SendMessage (message, wParam, lParam) ;

      pAlert = (ALERT*)lParam ;

      // no alert when disabled
      // accept errors and warnings
      if( state==STATE_DISABLED && 
	  pAlert->id!=ALERT_ERROR && 
	  pAlert->id!=ALERT_WARNING )
	return 0 ;

      if( state==STATE_ALERT )
	KillTimer (hwnd, ID_BALLOON_TIMER) ;      

      // play sound
      if( pAlert->id>=ALERT_CLASS_REGISTRY && state!=STATE_ALERT )	
	if( Config_GetInteger(CFGINT_ENABLE_SOUND) )
	  PlaySound (MAKEINTRESOURCE(IDW_POOCH), hInstance, SND_ASYNC|SND_RESOURCE) ;

      Alert_GetString (szNewMessage, MAX_MESSAGE_LEN, pAlert) ;
     
      if( Config_GetInteger(CFGINT_ENABLE_POOCHY_PICTURE) )
	{
	  if( state==STATE_ALERT && _tcslen(szBalloon)+_tcslen(szNewMessage)+2<MAX_ALERT_LEN ) 
	    {
	      _tcscat (szBalloon, TEXT("\r\n")) ;
	      _tcscat (szBalloon, szNewMessage) ;
	    }
	  else _tcsncpy (szBalloon, szNewMessage, MAX_ALERT_LEN) ;

	  ShowWindow (hwnd, SW_SHOW) ;
	  
	  InvalidateRect (hwnd, NULL, FALSE) ;
	  
	  GetWindowRect (hwnd, &rect) ;
	  SendMessage (hwndBalloon, TTM_TRACKACTIVATE, TRUE, (LPARAM)&tiBalloon) ;
	  SendMessage (hwndBalloon, TTM_TRACKPOSITION, 0, 
		       MAKELONG(rect.left+X_MOUTH,rect.top+Y_MOUTH)) ;
	  SendMessage (hwndBalloon, TTM_UPDATE, 0, 0) ;
  
	  nid.uFlags = NIF_ICON ;
	  nid.hIcon  = hAlertIcon ;
	  Shell_NotifyIcon (NIM_MODIFY, &nid) ;
 	}
      else
	{
	  if( state==STATE_ALERT && _tcslen(szBalloon)+_tcslen(szNewMessage)+2<255 ) 
	    {
	      _tcscat (szBalloon, TEXT("\r\n")) ;
	      _tcscat (szBalloon, szNewMessage) ;
	    }
	  else _tcsncpy (szBalloon, szNewMessage, 255) ;

	  _tcsncpy (nid.szInfo, szBalloon, 255) ;
	  _tcsncpy (nid.szInfoTitle, TEXT(APPLICATION_NAME), 64) ;      
	  nid.uFlags = NIF_INFO | NIF_ICON ;
	  nid.dwInfoFlags = NIIF_WARNING ;
	  nid.uTimeout = BALLOON_TIMEOUT ;
	  nid.hIcon  = hAlertIcon ;
	  Shell_NotifyIcon (NIM_MODIFY, &nid) ;
	}

      state = STATE_ALERT ;
      
      SetTimer (hwnd, ID_BALLOON_TIMER, BALLOON_TIMEOUT, NULL) ;

      return 0 ;

    case WM_NOTIFY:
      
      switch( ((NMHDR*)lParam)->code )
	{
	case TTN_GETDISPINFO:
	  ((LPNMTTDISPINFO)lParam)->lpszText = szBalloon ;
	  SendMessage (hwndBalloon, TTM_SETMAXTIPWIDTH, 0, 600);
	  break ;

	case TTN_SHOW:
	  SetWindowPos(hwndBalloon,
		       HWND_TOPMOST,
		       0, 0, 0, 0,
		       SWP_NOMOVE|SWP_NOSIZE);
	  break ;
	}
	return 0 ;

    default:

      if( message == uTaskbarCreatedMsg )
	{
	  nid.cbSize = sizeof(nid) ;
	  nid.hWnd   = hwnd ;
	  nid.uID    = ID_ICON ;
	  nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP ;
	  nid.uCallbackMessage = WM_SHELLNOTIFY ;
	  nid.hIcon  = 
	    state==STATE_DISABLED ? hDisabledIcon :
	    state==STATE_NORMAL ? aAnimIcons[0] : 
	    hAlertIcon ;
	  lstrcpy (nid.szTip, TEXT(APPLICATION_NAME)) ;
	  Shell_NotifyIcon (NIM_ADD, &nid) ;      
	}
      
    } // switch( message )

  return DefWindowProc (hwnd, message, wParam, lParam) ;
}
